const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

export class MyPromise {
    status = PENDING;
    // 成功之后的值
    value = undefined;
    // 失败之后的原因
    reason = undefined;
    successCallback = [];
    failCallback = [];

    resolve = (value) => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        // this.successCallback?.(this.value)
        // 处理多次then的情况
        while (this.successCallback.length) {
            this.successCallback.shift()()
        }
    }
    reject = (reason) => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        // this.failCallback?.(this.reason)
        while (this.failCallback.length) {
            this.failCallback.shift()()
        }
    }

    constructor(executor) {
        try {
            // 创建时立即执行 executor
            executor(this.resolve, this.reject);
        } catch (error) {
            this.reject(error)
        }
    }

    then(successCallback, failCallback) {
        successCallback = getCallbackWithBackUp(successCallback)
        failCallback = getCallbackWithBackUp(failCallback)
        let nextPromise = new MyPromise((resolve, reject) => {
            if (this.status == FULFILLED) {
                // 使内部能够拿到rtn
                queueMicrotask(() => {
                    try {
                        let success_rtn = successCallback(this.value)
                        // resolve(success_rtn)
                        resolvePromise(nextPromise, success_rtn, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            } else if (this.status == REJECTED) {
                queueMicrotask(() => {
                    try {
                        let success_rtn = failCallback(this.reason)
                        // resolve(success_rtn)
                        resolvePromise(nextPromise, success_rtn, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            } else { // pending
                // 存储成功和失败回调
                this.successCallback.push(() => {
                    queueMicrotask(() => {
                        try {
                            let success_rtn = successCallback(this.value)
                            // resolve(success_rtn)
                            resolvePromise(nextPromise, success_rtn, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                });
                this.failCallback.push(() => {
                    queueMicrotask(() => {
                        try {
                            let success_rtn = failCallback(this.reason)
                            // resolve(success_rtn)
                            resolvePromise(nextPromise, success_rtn, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                });
            }

        })
        return nextPromise;
    }
    catch(failCallback) {
        return this.then(undefined, failCallback)
    }
    finally(callback) {
        return this.then((value) => {
            return MyPromise.resolve(callback()).then(() => value)
        }, (reason) => {
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
    }

    static all(array) {
        let result = []
        let counter = 0
        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                result[key] = value
                counter++
                if (counter === array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i]
                if (current instanceof MyPromise) {
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    addData(i, array[i])
                }
            }
        })
    }
    static allSettled(array) {
        let result = []
        let counter = 0
        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                result[key] = value
                counter++
                if (counter === array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i]
                if (current instanceof MyPromise) {
                    current.then(value => addData(i, value), reason => addData(i, value))
                } else {
                    addData(i, array[i])
                }
            }
        })
    }
    static race(array) {
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < array.length; i++) {
                let current = array[i]
                if (current instanceof MyPromise) {
                    current.then(value => resolve(current), reason => reject(reason))
                } else {
                    resolve(current)
                }
            }
        })
    }

    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        } else {
            return new MyPromise((resolve, reject) => {
                resolve(value)
            })
        }
    }


}


/**
 * 判断 value 是否为 promise, 为普通值直接 resolve, 为promise则根据返回结果，
 * 决定调用 resolve 或 reject
 * @param value 用于判断的值
 * @param resolve 
 * @param reject 
 */
function resolvePromise(rtnPromise, value, resolve, reject) {
    if (rtnPromise === value) {
        return reject(new TypeError('Chaining Circle ...'))
    }
    if (value instanceof MyPromise) {
        value.then(resolve, reject)
    } else {
        resolve(value)
    }
}

/**
 * 对不符合条件的回调函数的值，进行回退
 * @param callback 回调
 */
function getCallbackWithBackUp(callback) {
    if (callback instanceof Function) {
        return callback
    }
    return value => value
}